using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace LuaInterface
{
	/*
	 * Structure to store a type and the return types of
	 * its methods (the type of the returned value and out/ref
	 * parameters).
	 */
	struct LuaClassType 
	{
		public Type klass;
		public Type[][] returnTypes;
	}

	/*
	 * Common interface for types generated from tables. The method
	 * returns the table that overrides some or all of the type's methods.
	 */
	public interface ILuaGeneratedType 
	{
		LuaTable __luaInterface_getLuaTable();
	}

	/*
	 * Class used for generating delegates that get a function from the Lua
	 * stack as a delegate of a specific type.
	 * 
	 * Author: Fabio Mascarenhas
	 * Version: 1.0
	 */
	class DelegateGenerator 
	{
		private ObjectTranslator translator;
		private Type delegateType;
		
		public DelegateGenerator(ObjectTranslator translator,Type delegateType) 
		{
			this.translator=translator;
			this.delegateType=delegateType;
		}
		public object extractGenerated(IntPtr luaState,int stackPos)
		{
			return CodeGeneration.Instance.GetDelegate(delegateType,translator.getFunction(luaState,stackPos));
		}
	}

	/*
	 * Class used for generating delegates that get a table from the Lua
	 * stack as a an object of a specific type.
	 * 
	 * Author: Fabio Mascarenhas
	 * Version: 1.0
	 */
	class ClassGenerator 
	{
		private ObjectTranslator translator;
		private Type klass;
		
		public ClassGenerator(ObjectTranslator translator,Type klass) 
		{
			this.translator=translator;
			this.klass=klass;
		}
		public object extractGenerated(IntPtr luaState,int stackPos)
		{
			return CodeGeneration.Instance.GetClassInstance(klass,translator.getTable(luaState,stackPos));
		}
	}

	/*
	 * Dynamically generates new types from existing types and
	 * Lua function and table values. Generated types are event handlers,
	 * delegates, interface implementations and subclasses.
	 * 
	 * Author: Fabio Mascarenhas
	 * Version: 1.0
	 */
	class CodeGeneration
	{
		private Type eventHandlerParent=typeof(LuaEventHandler);
		private Dictionary<Type, Type> eventHandlerCollection=new Dictionary<Type, Type>();

		private Type delegateParent=typeof(LuaDelegate);
		private Dictionary<Type, Type> delegateCollection=new Dictionary<Type, Type>();

		private Type classHelper=typeof(LuaClassHelper);
		private Dictionary<Type, LuaClassType> classCollection=new Dictionary<Type, LuaClassType>();

		private AssemblyName assemblyName;
		private AssemblyBuilder newAssembly;
		private ModuleBuilder newModule;
		private int luaClassNumber=1;
		private static readonly  CodeGeneration instance = new CodeGeneration();

		static CodeGeneration()
		{
		}
		
		private CodeGeneration() 
		{
			// Create an assembly name
			assemblyName=new AssemblyName( );
			assemblyName.Name="LuaInterface_generatedcode";
			// Create a new assembly with one module.
			newAssembly=Thread.GetDomain().DefineDynamicAssembly(
				assemblyName, AssemblyBuilderAccess.Run);
			newModule=newAssembly.DefineDynamicModule("LuaInterface_generatedcode");
		}

		/*
		 * Singleton instance of the class
		 */
		public static CodeGeneration Instance 
		{
			get 
			{
				return instance;
			}
		}
		
		/*
		 *  Generates an event handler that calls a Lua function
		 */
		private Type GenerateEvent(Type eventHandlerType)
		{
			string typeName;
			lock(this) 
			{
				typeName = "LuaGeneratedClass" + luaClassNumber;
				luaClassNumber++;
			}
			// Define a public class in the assembly, called typeName
			TypeBuilder myType=newModule.DefineType(typeName,TypeAttributes.Public,eventHandlerParent);

			// Defines the handler method. Its signature is void(object,<subclassofEventArgs>)
			Type[] paramTypes = new Type[2];
			paramTypes[0]=typeof(object);
			paramTypes[1]=eventHandlerType;
			Type returnType=typeof(void);
			MethodBuilder handleMethod=myType.DefineMethod("HandleEvent",
				MethodAttributes.Public|MethodAttributes.HideBySig,
				returnType,paramTypes);
 
			// Emits the IL for the method. It loads the arguments
			// and calls the handleEvent method of the base class
			ILGenerator generator=handleMethod.GetILGenerator( );
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldarg_1);
			generator.Emit(OpCodes.Ldarg_2);
			MethodInfo miGenericEventHandler;
			miGenericEventHandler=eventHandlerParent.GetMethod("handleEvent");
			generator.Emit(OpCodes.Call,miGenericEventHandler);
			// returns
			generator.Emit(OpCodes.Ret);
  
			// creates the new type
			return myType.CreateType();
		}

		/*
		 * Generates a type that can be used for instantiating a delegate
		 * of the provided type, given a Lua function.
		 */
		private Type GenerateDelegate(Type delegateType)
		{
			string typeName;
			lock(this) 
			{
				typeName = "LuaGeneratedClass" + luaClassNumber;
				luaClassNumber++;
			}
			// Define a public class in the assembly, called typeName
			TypeBuilder myType=newModule.DefineType(typeName,TypeAttributes.Public,delegateParent);
 
			// Defines the delegate method with the same signature as the
			// Invoke method of delegateType
			MethodInfo invokeMethod=delegateType.GetMethod("Invoke");
			ParameterInfo[] paramInfo=invokeMethod.GetParameters();
			Type[] paramTypes=new Type[paramInfo.Length];
			Type returnType=invokeMethod.ReturnType;

			// Counts out and ref params, for use later
			int nOutParams=0; int nOutAndRefParams=0;
			for(int i=0;i<paramTypes.Length;i++) 
			{
				paramTypes[i]=paramInfo[i].ParameterType;
				if((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
					nOutParams++;
				if(paramTypes[i].IsByRef)
					nOutAndRefParams++;
			}
			int[] refArgs=new int[nOutAndRefParams];

			MethodBuilder delegateMethod=myType.DefineMethod("CallFunction",
				invokeMethod.Attributes,
				returnType,paramTypes);

			// Generates the IL for the method
			ILGenerator generator=delegateMethod.GetILGenerator( );
 
			generator.DeclareLocal(typeof(object[])); // original arguments
			generator.DeclareLocal(typeof(object[])); // with out-only arguments removed
			generator.DeclareLocal(typeof(int[])); // indexes of out and ref arguments
			if(!(returnType == typeof(void)))  // return value
				generator.DeclareLocal(returnType);
			else
				generator.DeclareLocal(typeof(object));
			// Initializes local variables
			generator.Emit(OpCodes.Ldc_I4,paramTypes.Length);
			generator.Emit(OpCodes.Newarr,typeof(object));
			generator.Emit(OpCodes.Stloc_0);
			generator.Emit(OpCodes.Ldc_I4,paramTypes.Length-nOutParams);
			generator.Emit(OpCodes.Newarr,typeof(object));
			generator.Emit(OpCodes.Stloc_1);
			generator.Emit(OpCodes.Ldc_I4,nOutAndRefParams);
			generator.Emit(OpCodes.Newarr,typeof(int));
			generator.Emit(OpCodes.Stloc_2);
			// Stores the arguments in the local variables
			for(int iArgs=0,iInArgs=0,iOutArgs=0;iArgs<paramTypes.Length;iArgs++) 
			{
				generator.Emit(OpCodes.Ldloc_0);
				generator.Emit(OpCodes.Ldc_I4,iArgs);
				generator.Emit(OpCodes.Ldarg,iArgs+1);
				if(paramTypes[iArgs].IsByRef)
				{
					if(paramTypes[iArgs].GetElementType().IsValueType)
					{
						generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
						generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
					} else generator.Emit(OpCodes.Ldind_Ref);
				}
				else 
				{
					if(paramTypes[iArgs].IsValueType)
						generator.Emit(OpCodes.Box,paramTypes[iArgs]);
				}
				generator.Emit(OpCodes.Stelem_Ref);
				if(paramTypes[iArgs].IsByRef) 
				{
					generator.Emit(OpCodes.Ldloc_2);
					generator.Emit(OpCodes.Ldc_I4,iOutArgs);
					generator.Emit(OpCodes.Ldc_I4,iArgs);
					generator.Emit(OpCodes.Stelem_I4);
					refArgs[iOutArgs]=iArgs;
					iOutArgs++;
				}
				if(paramInfo[iArgs].IsIn || (!paramInfo[iArgs].IsOut)) 
				{
					generator.Emit(OpCodes.Ldloc_1);
					generator.Emit(OpCodes.Ldc_I4,iInArgs);
					generator.Emit(OpCodes.Ldarg,iArgs+1);
					if(paramTypes[iArgs].IsByRef)
					{
						if(paramTypes[iArgs].GetElementType().IsValueType) 
						{
							generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
							generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
						}
						else generator.Emit(OpCodes.Ldind_Ref);
					}
					else 
					{
						if(paramTypes[iArgs].IsValueType)
							generator.Emit(OpCodes.Box,paramTypes[iArgs]);
					}
					generator.Emit(OpCodes.Stelem_Ref);
					iInArgs++;
				} 
			}
			// Calls the callFunction method of the base class
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldloc_0);
			generator.Emit(OpCodes.Ldloc_1);
			generator.Emit(OpCodes.Ldloc_2);
			MethodInfo miGenericEventHandler;
			miGenericEventHandler=delegateParent.GetMethod("callFunction");
			generator.Emit(OpCodes.Call,miGenericEventHandler);
			// Stores return value
			if(returnType == typeof(void)) 
			{
				generator.Emit(OpCodes.Pop);
				generator.Emit(OpCodes.Ldnull);
			}
			else if(returnType.IsValueType) 
			{
				generator.Emit(OpCodes.Unbox,returnType);
				generator.Emit(OpCodes.Ldobj,returnType);
			} else generator.Emit(OpCodes.Castclass,returnType);
			generator.Emit(OpCodes.Stloc_3);
			// Stores new value of out and ref params
			for(int i=0;i<refArgs.Length;i++) 
			{
				generator.Emit(OpCodes.Ldarg,refArgs[i]+1);
				generator.Emit(OpCodes.Ldloc_0);
				generator.Emit(OpCodes.Ldc_I4,refArgs[i]);
				generator.Emit(OpCodes.Ldelem_Ref);
				if(paramTypes[refArgs[i]].GetElementType().IsValueType) 
				{
					generator.Emit(OpCodes.Unbox,paramTypes[refArgs[i]].GetElementType());
					generator.Emit(OpCodes.Ldobj,paramTypes[refArgs[i]].GetElementType());
					generator.Emit(OpCodes.Stobj,paramTypes[refArgs[i]].GetElementType());
				} 
				else 
				{
					generator.Emit(OpCodes.Castclass,paramTypes[refArgs[i]].GetElementType());
					generator.Emit(OpCodes.Stind_Ref);
				}
			}
			// Returns
			if(!(returnType == typeof(void)))
				generator.Emit(OpCodes.Ldloc_3);
			generator.Emit(OpCodes.Ret);
  
			// creates the new type
			return myType.CreateType();
		}

		/*
		 * Generates an implementation of klass, if it is an interface, or
		 * a subclass of klass that delegates its virtual methods to a Lua table.
		 */
		public void GenerateClass(Type klass,out Type newType,out Type[][] returnTypes)
		{
			string typeName;
			lock(this) 
			{
				typeName = "LuaGeneratedClass" + luaClassNumber;
				luaClassNumber++;
			}
			TypeBuilder myType;
			// Define a public class in the assembly, called typeName
			if(klass.IsInterface)
				myType=newModule.DefineType(typeName,TypeAttributes.Public,typeof(object),new Type[] { klass,typeof(ILuaGeneratedType) });
			else
				myType=newModule.DefineType(typeName,TypeAttributes.Public,klass,new Type[] { typeof(ILuaGeneratedType) });
			// Field that stores the Lua table
			FieldBuilder luaTableField=myType.DefineField("__luaInterface_luaTable",typeof(LuaTable),FieldAttributes.Public);
			// Field that stores the return types array
			FieldBuilder returnTypesField=myType.DefineField("__luaInterface_returnTypes",typeof(Type[][]),FieldAttributes.Public);
			// Generates the constructor for the new type, it takes a Lua table and an array
			// of return types and stores them in the respective fields
			ConstructorBuilder constructor=
				myType.DefineConstructor(MethodAttributes.Public,CallingConventions.Standard,new Type[] { typeof(LuaTable),typeof(Type[][]) });
			ILGenerator generator=constructor.GetILGenerator();
			generator.Emit(OpCodes.Ldarg_0);
			if(klass.IsInterface)
				generator.Emit(OpCodes.Call,typeof(object).GetConstructor(Type.EmptyTypes));
			else
				generator.Emit(OpCodes.Call,klass.GetConstructor(Type.EmptyTypes));
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldarg_1);
			generator.Emit(OpCodes.Stfld,luaTableField);
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldarg_2);
			generator.Emit(OpCodes.Stfld,returnTypesField);
			generator.Emit(OpCodes.Ret);
			// Generates overriden versions of the klass' public virtual methods
			MethodInfo[] classMethods=klass.GetMethods();
			returnTypes=new Type[classMethods.Length][];
			int i=0;
			foreach(MethodInfo method in classMethods) 
			{
				if(klass.IsInterface) 
				{
					GenerateMethod(myType,method,
						MethodAttributes.HideBySig|MethodAttributes.Virtual|MethodAttributes.NewSlot,
						i,luaTableField,returnTypesField,false,out returnTypes[i]);
					i++;
				} 
				else 
				{
					if(!method.IsPrivate && !method.IsFinal && method.IsVirtual) 
					{
						GenerateMethod(myType,method,(method.Attributes|MethodAttributes.NewSlot)^MethodAttributes.NewSlot,i,
							luaTableField,returnTypesField,true,out returnTypes[i]);
						i++;
					}
				}
			}
			// Generates an implementation of the __luaInterface_getLuaTable method
			MethodBuilder returnTableMethod=myType.DefineMethod("__luaInterface_getLuaTable",
				MethodAttributes.Public|MethodAttributes.HideBySig|MethodAttributes.Virtual,
				typeof(LuaTable),new Type[0]);
			myType.DefineMethodOverride(returnTableMethod,typeof(ILuaGeneratedType).GetMethod("__luaInterface_getLuaTable"));
			generator=returnTableMethod.GetILGenerator();
			generator.Emit(OpCodes.Ldfld,luaTableField);
			generator.Emit(OpCodes.Ret);
			// Creates the type
			newType=myType.CreateType();
		}

		/*
		 * Generates an overriden implementation of method inside myType that delegates
		 * to a function in a Lua table with the same name, if the function exists. If it
		 * doesn't the method calls the base method (or does nothing, in case of interface
		 * implementations).
		 */
		private void GenerateMethod(TypeBuilder myType,MethodInfo method,MethodAttributes attributes,
			int methodIndex,FieldInfo luaTableField,FieldInfo returnTypesField,bool generateBase,out Type[] returnTypes)
		{
			ParameterInfo[] paramInfo=method.GetParameters();
			Type[] paramTypes=new Type[paramInfo.Length];
			List<Type> returnTypesList=new List<Type>();

			// Counts out and ref parameters, for later use,
			// and creates the list of return types
			int nOutParams=0; int nOutAndRefParams=0;
			Type returnType=method.ReturnType;
			returnTypesList.Add(returnType);
			for(int i=0;i<paramTypes.Length;i++) 
			{
				paramTypes[i]=paramInfo[i].ParameterType;
				if((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
					nOutParams++;
				if(paramTypes[i].IsByRef) 
				{
					returnTypesList.Add(paramTypes[i].GetElementType());
					nOutAndRefParams++;
				}
			}
			int[] refArgs=new int[nOutAndRefParams];
			returnTypes=returnTypesList.ToArray();

			// Generates a version of the method that calls the base implementation
			// directly, for use by the base field of the table
			if(generateBase) 
			{
				MethodBuilder baseMethod=myType.DefineMethod("__luaInterface_base_"+method.Name,
					MethodAttributes.Private|MethodAttributes.NewSlot|MethodAttributes.HideBySig,
					returnType,paramTypes);
				ILGenerator generatorBase=baseMethod.GetILGenerator();
				generatorBase.Emit(OpCodes.Ldarg_0);
				for(int i=0;i<paramTypes.Length;i++)
					generatorBase.Emit(OpCodes.Ldarg,i+1);
				generatorBase.Emit(OpCodes.Call,method);
				if(returnType == typeof(void))
					generatorBase.Emit(OpCodes.Pop);
				generatorBase.Emit(OpCodes.Ret);
			}

			// Defines the method
			MethodBuilder methodImpl=myType.DefineMethod(method.Name,attributes,
				returnType,paramTypes);
			// If it's an implementation of an interface tells what method it
			// is overriding
			if(myType.BaseType.Equals(typeof(object)))
				myType.DefineMethodOverride(methodImpl,method);
 
			ILGenerator generator=methodImpl.GetILGenerator( );
 
			generator.DeclareLocal(typeof(object[])); // original arguments
			generator.DeclareLocal(typeof(object[])); // with out-only arguments removed
			generator.DeclareLocal(typeof(int[])); // indexes of out and ref arguments
			if(!(returnType == typeof(void))) // return value
				generator.DeclareLocal(returnType);
			else
				generator.DeclareLocal(typeof(object));
			// Initializes local variables
			generator.Emit(OpCodes.Ldc_I4,paramTypes.Length);
			generator.Emit(OpCodes.Newarr,typeof(object));
			generator.Emit(OpCodes.Stloc_0);
			generator.Emit(OpCodes.Ldc_I4,paramTypes.Length-nOutParams+1);
			generator.Emit(OpCodes.Newarr,typeof(object));
			generator.Emit(OpCodes.Stloc_1);
			generator.Emit(OpCodes.Ldc_I4,nOutAndRefParams);
			generator.Emit(OpCodes.Newarr,typeof(int));
			generator.Emit(OpCodes.Stloc_2);
			generator.Emit(OpCodes.Ldloc_1);
			generator.Emit(OpCodes.Ldc_I4_0);
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldfld,luaTableField);
			generator.Emit(OpCodes.Stelem_Ref);
			// Stores the arguments into the local variables, as needed
			for(int iArgs=0,iInArgs=1,iOutArgs=0;iArgs<paramTypes.Length;iArgs++) 
			{
				generator.Emit(OpCodes.Ldloc_0);
				generator.Emit(OpCodes.Ldc_I4,iArgs);
				generator.Emit(OpCodes.Ldarg,iArgs+1);
				if(paramTypes[iArgs].IsByRef)
				{
					if(paramTypes[iArgs].GetElementType().IsValueType)
					{
						generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
						generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
					} 
					else generator.Emit(OpCodes.Ldind_Ref);
				}
				else 
				{
					if(paramTypes[iArgs].IsValueType)
						generator.Emit(OpCodes.Box,paramTypes[iArgs]);
				}
				generator.Emit(OpCodes.Stelem_Ref);
				if(paramTypes[iArgs].IsByRef) 
				{
					generator.Emit(OpCodes.Ldloc_2);
					generator.Emit(OpCodes.Ldc_I4,iOutArgs);
					generator.Emit(OpCodes.Ldc_I4,iArgs);
					generator.Emit(OpCodes.Stelem_I4);
					refArgs[iOutArgs]=iArgs;
					iOutArgs++;
				}
				if(paramInfo[iArgs].IsIn || (!paramInfo[iArgs].IsOut)) 
				{
					generator.Emit(OpCodes.Ldloc_1);
					generator.Emit(OpCodes.Ldc_I4,iInArgs);
					generator.Emit(OpCodes.Ldarg,iArgs+1);
					if(paramTypes[iArgs].IsByRef)
					{
						if(paramTypes[iArgs].GetElementType().IsValueType) 
						{
							generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
							generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
						}
						else generator.Emit(OpCodes.Ldind_Ref);
					}
					else 
					{
						if(paramTypes[iArgs].IsValueType)
							generator.Emit(OpCodes.Box,paramTypes[iArgs]);
					}
					generator.Emit(OpCodes.Stelem_Ref);
					iInArgs++;
				} 
			}
			// Gets the function the method will delegate to by calling
			// the getTableFunction method of class LuaClassHelper
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldfld,luaTableField);
			generator.Emit(OpCodes.Ldstr,method.Name);
			generator.Emit(OpCodes.Call,classHelper.GetMethod("getTableFunction"));
			Label lab1=generator.DefineLabel();
			generator.Emit(OpCodes.Dup);
			generator.Emit(OpCodes.Brtrue_S,lab1);
			// Function does not exist, call base method
			generator.Emit(OpCodes.Pop);
			if(!method.IsAbstract) 
			{
				generator.Emit(OpCodes.Ldarg_0);
				for(int i=0;i<paramTypes.Length;i++)
					generator.Emit(OpCodes.Ldarg,i+1);
				generator.Emit(OpCodes.Call,method);
				if(returnType == typeof(void)) 
					generator.Emit(OpCodes.Pop);
				generator.Emit(OpCodes.Ret);
				generator.Emit(OpCodes.Ldnull);
			} else
				generator.Emit(OpCodes.Ldnull);
			Label lab2=generator.DefineLabel();
			generator.Emit(OpCodes.Br_S,lab2);
			generator.MarkLabel(lab1);
			// Function exists, call using method callFunction of LuaClassHelper
			generator.Emit(OpCodes.Ldloc_0);
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldfld,returnTypesField);
			generator.Emit(OpCodes.Ldc_I4,methodIndex);
			generator.Emit(OpCodes.Ldelem_Ref);
			generator.Emit(OpCodes.Ldloc_1);
			generator.Emit(OpCodes.Ldloc_2);
			generator.Emit(OpCodes.Call,classHelper.GetMethod("callFunction"));
			generator.MarkLabel(lab2);
			// Stores the function return value
			if(returnType == typeof(void)) 
			{
				generator.Emit(OpCodes.Pop);
				generator.Emit(OpCodes.Ldnull);
			}
			else if(returnType.IsValueType) 
			{
				generator.Emit(OpCodes.Unbox,returnType);
				generator.Emit(OpCodes.Ldobj,returnType);
			} 
			else generator.Emit(OpCodes.Castclass,returnType);
			generator.Emit(OpCodes.Stloc_3);
			// Sets return values of out and ref parameters
			for(int i=0;i<refArgs.Length;i++) 
			{
				generator.Emit(OpCodes.Ldarg,refArgs[i]+1);
				generator.Emit(OpCodes.Ldloc_0);
				generator.Emit(OpCodes.Ldc_I4,refArgs[i]);
				generator.Emit(OpCodes.Ldelem_Ref);
				if(paramTypes[refArgs[i]].GetElementType().IsValueType) 
				{
					generator.Emit(OpCodes.Unbox,paramTypes[refArgs[i]].GetElementType());
					generator.Emit(OpCodes.Ldobj,paramTypes[refArgs[i]].GetElementType());
					generator.Emit(OpCodes.Stobj,paramTypes[refArgs[i]].GetElementType());
				} 
				else 
				{
					generator.Emit(OpCodes.Castclass,paramTypes[refArgs[i]].GetElementType());
					generator.Emit(OpCodes.Stind_Ref);
				}
			}

			// Returns
			if(!(returnType == typeof(void)))
				generator.Emit(OpCodes.Ldloc_3);
			generator.Emit(OpCodes.Ret);
		}

		/*
		 * Gets an event handler for the event type that delegates to the eventHandler Lua function.
		 * Caches the generated type.
		 */
		public LuaEventHandler GetEvent(Type eventHandlerType, LuaFunction eventHandler)
		{
			Type eventConsumerType;
			if (eventHandlerCollection.ContainsKey(eventHandlerType)) 
			{
				eventConsumerType=eventHandlerCollection[eventHandlerType];
			} 
			else 
			{
				eventConsumerType=GenerateEvent(eventHandlerType);
				eventHandlerCollection[eventHandlerType] = eventConsumerType;
			}
			LuaEventHandler luaEventHandler=(LuaEventHandler)Activator.CreateInstance(eventConsumerType);
			luaEventHandler.handler=eventHandler;
			return luaEventHandler;
		}

		/*
		 * Gets a delegate with delegateType that calls the luaFunc Lua function
		 * Caches the generated type.
		 */
		public Delegate GetDelegate(Type delegateType, LuaFunction luaFunc)
		{
			List<Type> returnTypes=new List<Type>();
			Type luaDelegateType;
			if (delegateCollection.ContainsKey(delegateType)) 
			{
				luaDelegateType=delegateCollection[delegateType];
			} 
			else 
			{
				luaDelegateType=GenerateDelegate(delegateType);
				delegateCollection[delegateType] = luaDelegateType;
			}
			MethodInfo methodInfo=delegateType.GetMethod("Invoke");
			returnTypes.Add(methodInfo.ReturnType);
			foreach(ParameterInfo paramInfo in methodInfo.GetParameters()) 
				if(paramInfo.ParameterType.IsByRef)
					returnTypes.Add(paramInfo.ParameterType);
			LuaDelegate luaDelegate=(LuaDelegate)Activator.CreateInstance(luaDelegateType);
			luaDelegate.function=luaFunc;
			luaDelegate.returnTypes=returnTypes.ToArray();
			return Delegate.CreateDelegate(delegateType,luaDelegate,"CallFunction");
		}

		/*
		 * Gets an instance of an implementation of the klass interface or
		 * subclass of klass that delegates public virtual methods to the
		 * luaTable table.
		 * Caches the generated type.
		 */
		public object GetClassInstance(Type klass, LuaTable luaTable)
		{
			LuaClassType luaClassType;
			if (classCollection.ContainsKey(klass)) 
			{
				luaClassType=classCollection[klass];
			} 
			else 
			{
				luaClassType=new LuaClassType();
				GenerateClass(klass,out luaClassType.klass,out luaClassType.returnTypes);
				classCollection[klass] = luaClassType;
			}
			return Activator.CreateInstance(luaClassType.klass,new object[] {luaTable,luaClassType.returnTypes});
		}
	}
}
